package org.uma.jmetal.example.operator;

import org.uma.jmetal.lab.plot.PlotFront;
import org.uma.jmetal.lab.plot.impl.PlotSmile;
import org.uma.jmetal.operator.crossover.CrossoverOperator;
import org.uma.jmetal.operator.crossover.impl.IntegerSBXCrossover;
import org.uma.jmetal.problem.integerproblem.IntegerProblem;
import org.uma.jmetal.problem.multiobjective.NMMin;
import org.uma.jmetal.solution.integersolution.IntegerSolution;
import org.uma.jmetal.util.JMetalLogger;
import org.uma.jmetal.util.comparator.IntegerVariableComparator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author Antonio J. Nebro
 * @version 1.0
 *
 *此类旨在验证SBX交叉运算符对Integer编码的工作情况
 *
 *该图描述了生成100000点，粒度200和数字时获得的值
 *可以在此处找到不同分布指数值（5、10、20）的*：
 * <a href="https://github.com/jMetal/jMetal/blob/master/figures/integerPolynomialMutation.png">
 *多项式突变（整数）</a>
 */
public class IntegerSBXCrossoverExample {
  /**
   *程序生成代表SBX生成的点分布的数据
   *交叉运算符。 命令行要引入的参数是：
   *-numberOfSolutions：要生成的解决方案的数量
   *-粒度：要考虑的细分数量。
   *-distributionIndex：多项式变异算子的分布指数
   *-outputFile：包含结果的文件
   *
   * @param args Command line arguments
   */
  public static void main(String[] args) {
    int numberOfPoints ;
    int granularity ;
    double distributionIndex ;

    if (args.length !=3) {
      JMetalLogger.logger.info("Usage: numberOfSolutions granularity distributionIndex") ;
      JMetalLogger.logger.info("Using default parameters") ;

      numberOfPoints = 10000 ;
      granularity = 100 ;
      distributionIndex = 10.0 ;
    } else {
      numberOfPoints = Integer.parseInt(args[0]);
      granularity = Integer.parseInt(args[1]);
      distributionIndex = Double.parseDouble(args[2]);
    }

    IntegerProblem problem ;

    problem = new NMMin(1, -50, 50, -100, 100) ;
    CrossoverOperator<IntegerSolution> crossover = new IntegerSBXCrossover(1.0, distributionIndex) ;

    IntegerSolution solution1 = problem.createSolution() ;
    IntegerSolution solution2 = problem.createSolution() ;
    solution1.setVariable(0, -50);
    solution2.setVariable(0, 50);
    List<IntegerSolution> parents = Arrays.asList(solution1, solution2) ;

    List<IntegerSolution> population = new ArrayList<>(numberOfPoints) ;
    for (int i = 0; i < numberOfPoints ; i++) {
      List<IntegerSolution> solutions = (List<IntegerSolution>) crossover.execute(parents);
      population.add(solutions.get(0)) ;
      population.add(solutions.get(1)) ;
    }

    Collections.sort(population, new IntegerVariableComparator()) ;

    double[][] classifier = classify(population, problem, granularity);

    PlotFront plot = new PlotSmile(classifier) ;
    plot.plot();
  }

  private static double[][] classify(List<IntegerSolution> solutions, IntegerProblem problem, int granularity) {
    double grain = (problem.getUpperBound(0) - problem.getLowerBound(0)) / granularity ;
    double[][] classifier = new double[granularity][] ;
    for (int i = 0 ; i < granularity; i++) {
      classifier[i] = new double[2] ;
      classifier[i][0] = problem.getLowerBound(0) + i * grain ;
      classifier[i][1] = 0 ;
    }

    for (IntegerSolution solution : solutions) {
      boolean found = false ;
      int index = 0 ;
      while (!found) {
        if (solution.getVariable(0) <= classifier[index][0]) {
          classifier[index][1] ++ ;
          found = true ;
        } else {
          if (index == (granularity - 1)) {
            classifier[index][1] ++ ;
            found = true ;
          } else {
            index++;
          }
        }
      }
    }

    return classifier ;
  }
}
